package ini

import (
	"io"
	"fmt"
	"strings"
)

const (
	bufferSize = 4096
)

// getTypeTable返回一个表示字符到字符类型的映射表
func getTypeTable(comment byte) [256]int {
	var t [256]int

	for i := 0; i < 256; i++ {
		t[i] = -1
	}
	for i := 32; i <= 126; i++ {
		t[i] = 6
	}
	for i:= 'a'; i <= 'z'; i++ {
		t[i] = 0
	}
	for i:= 'A'; i <= 'Z'; i++ {
		t[i] = 0
	}
	t['_'] = 0
	for i:= '0'; i <= '9'; i++ {
		t[i] = 1
	}

	t['='] = 2
	t['['] = 3
	t[']'] = 4
	t[comment] = 5

	t[' '] = 7
	t['\t'] = 7

	t[0] = 8
	t['\n'] = 8
	return t
}

// getDfa返回一个DFA
func getDfa() [9][9]int {
	return [9][9]int {
		{ 1, -1, -1,  5, -1,  8, -1, 0,  0},
		{ 1,  1,  3, -1, -1, -1, -1, 2, -1},
		{-1, -1,  3, -1, -1, -1, -1, 2, -1},
		{ 4,  4,  4,  4,  4,  4,  4, 4, -1},
		{ 4,  4,  4,  4,  4,  4,  4, 4,  0},
		{ 6, -1, -1, -1, -1, -1, -1,-1, -1},
		{ 6,  6, -1, -1,  7, -1, -1,-1, -1},
		{-1, -1, -1, -1, -1, -1, -1, 7,  0},
		{ 8,  8,  8,  8,  8,  8,  8, 8,  0},
	}
}

// 装载第bid个缓冲区（双缓冲分析方案）
func loadBuffer(buffer []byte, in io.Reader, bid int) {
	start := (bufferSize + 1) * bid
	end   := (bufferSize + 1) * (bid + 1) - 1
	n, _ := in.Read(buffer[start:end])
	buffer[start + n] = 0
}

// parse把给定输入源转化为File，ini包的核心算法在此
func parse(in io.Reader, comment byte) (*File, error) {
	buffer := make([]byte, 2 * (bufferSize + 1))
	forward := 2 * (bufferSize + 1) - 1
	buffer[forward] = 0
	f := newFile()

	typeTable := getTypeTable(comment)
	dfa := getDfa()

	analyzing := true
	lineNumber := 1
	state := 0
	currentSection := ""
	keyName := ""
	valueName := ""
	for analyzing {
		c := buffer[forward]
		forward++
		if typeTable[c] == -1 {
			return nil, &errorSyntax{lineNumber, "非法字符"}
		}
		if c == 0 {
			analyzing = false
			for i := 0; i < 2; i++ {
				if forward == (i + 1) * (bufferSize + 1) {
					loadBuffer(buffer, in, i ^ 1)
					forward = (i ^ 1) * (bufferSize + 1)
					analyzing = true
					break
				}
			}
			if analyzing {
				continue
			}
		}

		newState := dfa[state][typeTable[c]]
		if newState == -1 {
			return nil, &errorSyntax{lineNumber, "语法错误"}
		}
		if state != newState {
			switch state {
				case 4:
					f.Section(currentSection).NewKey(keyName, strings.Trim(valueName, " \t"))
			}
		}
		switch newState {
			case 0:
				lineNumber++
				keyName = ""
				valueName = ""
			case 1:
				keyName = fmt.Sprintf("%s%c", keyName, c)
			case 4:
				valueName = fmt.Sprintf("%s%c", valueName, c)
			case 5:
				currentSection = ""
			case 6:
				currentSection = fmt.Sprintf("%s%c", currentSection, c)
		}

		state = newState
	}

	return f, nil
}





type errorSyntax struct {
	line int
	cause string
}

func (e *errorSyntax) Error() string {
	return fmt.Sprintf("第%d行语法错误：%s", e.line, e.cause)
}
